#include<stdio.h>
#include<string.h>

int main()
{
    /**
     * 声明字符串的两种方式
     *  方式一.将字符串声明成一个字符数组（如果采用第一种写法，字符数组的长度可以让编译器自动计算，声明时可以省略字符数组的长度。）
     *      1.在底层逻辑中，实际存储的时候，c语言还是会帮我们把字符串转换成字符数组进行保存，并且还要在末尾加上'\0'（表示字符串结束）
     *      2.数组的长度要么不写，如果要写就要记得把结束标记（'\0'）的空间给预留出来（一个字节）
     *      3.如果我们用字符数组+双引号的方式定义字符串，那么字符串的内容是可以改变的
     *  方式二.将字符串声明成一个指针（利用指针+双引号的方式定义字符串）
     *      1.在底层逻辑中，c语言还是会帮我们把字符串转换成字符串数组进行保存，并且在末尾加上'\0'
     *      2.利用指针+双引号的方式定义字符串的话会把底层的字符数组放在只读区
     *  只读常量区的特点：
     *      1.内容是不可以修改的
     *      2.里面定义的字符串是可以复用的（也就是说如果在我们创建了一个字符串之后又想要创建一个跟它一模一样的字符串，那么操作系统就不会再进行创建，而是让这两个字符串指针指向同一个位置，复用同一个数据）
     */
    // 方式一：将字符串声明成一个字符数组
    char s1[20] = "Hello, world!";  // 等价于  char s1[] = "Hello, world!";
    printf("s1 = %s\n", s1);
    // 方式二：将字符串声明成一个指针
    char* s2 = "Hello, world! s2~";
    printf("s2 = %s\n", s2);

    /**
     * 1.两种声明方式有何区别？
     *   区别1：指针指向的字符串，在C语言内部被当作常量，不能修改指针指向的字符串本身。修改了字符串的第一个字符，这种写法是错的，会
     *         导致难以预测的后果，执行时很可能会报错。
     *   区别2：指针变量可以指向其它字符串，字符数组变量不能指向另一个字符串。
     *
     * 2.为什么字符串声明为指针时不能修改，声明为数组时就可以修改？
     *   系统会将字符串的字面量保存在内存的常量区，这个区是不允许用户修改的。声明为指针时，指针变量存储的值是一个指向常量区的内存地址，
     *   因此用户不能通过这个地址去修改常量区。但是，声明为数组时，编译器会给数组单独分配一段内存，字符串字面量会被编译器解释成字符数
     *   组，逐个字符写入这段新分配的内存之中，而这段新内存是允许修改的
     *
     * 3.为什么数组变量不能赋值为另一个数组？（为什么给定义好的数组变量重新赋值会报错？）
     *   数组变量所在的地址无法改变，或者说，编译器一旦为数组变量分配地址后，这个地址就绑定这个数组变量了，这种绑定关系是不变的。C语言
     *   也因此规定，数组变量是一个不可修改的左值，即不能用赋值运算符为它重新赋值。
     *
     * 4.如何给定义好的数组变量重新赋值？
     *   想要重新赋值，必须使用C语言原生提供的strcpy()函数，通过字符串拷贝完成赋值。这样做以后，数组变量的地址还是不变的，即strcpy()
     *   只是在原地址写入新的字符串，而不是让数组变量指向新的地址。
     */
    char* s3 = "Hello, world! s3~";
    // 指针s3所指向的内容是常量，如果要改变这个常量的值，会报错
    //s3[0] = 'z'; // 错误
    printf("s3 = %s\n", s3);

    char s4[] = "Hello, world! s4~";
    s4[0] = 'O'; // 正确
    printf("s4 = %s\n", s4);

    // 为了提醒用户，字符串声明为指针后指向的内容不能修改，可以在声明时使用const说明符，保证该字符串是只读的
    const char* s5 = "Hello, world! s5~";
    printf("s5 = %s\n", s5);

    // 验证 指针变量可以指向其它字符串 s6指向的内容 "Hello, world!" 这个字符串是不能被改变的，但是 s6 可以指向一个新的字符串
    const char* s6 = "Hello, world!";
    s6 = "Hello, world! s6~";
    char* s7 = "Hello, world!";
    s7 = "Hello, world! s7~";
    printf("s6 = %s\n", s6);
    printf("s7 = %s\n", s7);

    // 验证 字符数组变量不能指向另一个字符串
    char s8[] = "Hello, world! s8~";
    //s8 = "Hello eclipse!"; // 报错，字符数组的数组名，总是指向初始化时的字符串地址，不能修改。
    printf("s8 = %s\n", s8);
    char s9[20];
    //s9 = "Hello, world! s9~"; // 错误，不能直接把字符串赋值给字符数组变量，会报错。原因是字符数组的变量名，跟定义时所指向的数组是绑定的，不能指向另一个地址。
    strcpy(s9, "Hello, world! s9~"); // 正确
    printf("s9 = %s\n", s9);
    return 0;
}
